package cn.jxau.fukin.service.impl;

import static cn.jxau.fukin.util.ConstantUtil.CURRENT;
import static cn.jxau.fukin.util.ConstantUtil.DEPOSIT;
import static cn.jxau.fukin.util.ConstantUtil.ERROR;
import static cn.jxau.fukin.util.ConstantUtil.ONEYEAR;
import static cn.jxau.fukin.util.ConstantUtil.PERIODICAL;
import static cn.jxau.fukin.util.ConstantUtil.SUCCESS;
import static cn.jxau.fukin.util.DateTimeUtil.getEndTime;
import static cn.jxau.fukin.util.DateTimeUtil.getParseDate;
import static cn.jxau.fukin.util.DateTimeUtil.getTotalDaysByDate;
import static cn.jxau.fukin.util.DateTimeUtil.getYearMonthAndDay;
import static cn.jxau.fukin.util.PKUtil.getPrimaryKey;

import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.jxau.fukin.dao.CardMapper;
import cn.jxau.fukin.dao.NotifyMapper;
import cn.jxau.fukin.dao.RateMapper;
import cn.jxau.fukin.dao.TradeMapper;
import cn.jxau.fukin.dao.UserBusinessMapper;
import cn.jxau.fukin.exception.ServiceException;
import cn.jxau.fukin.model.Card;
import cn.jxau.fukin.model.Notify;
import cn.jxau.fukin.model.Rate;
import cn.jxau.fukin.model.RateExample;
import cn.jxau.fukin.model.RateExample.Criteria;
import cn.jxau.fukin.model.Trade;
import cn.jxau.fukin.model.TradeExample;
import cn.jxau.fukin.model.User;
import cn.jxau.fukin.model.UserBusiness;
import cn.jxau.fukin.model.UserBusinessExample;
import cn.jxau.fukin.service.IRateService;
import cn.jxau.fukin.util.PKUtil;

@Service("rateService")
public class RateService implements IRateService {

	@Autowired
	RateMapper rateMapper;
	@Autowired
	UserBusinessMapper businessMapper;
	@Autowired
	TradeMapper tradeMapper;
	@Autowired
	CardMapper cardMapper;
	@Autowired
	NotifyMapper notifyMapper;

	public List<BigDecimal> calculatePreInterest(Rate rate, String money) {
		BigDecimal cash = new BigDecimal(money);
		List<BigDecimal> result = null;
		result = new LinkedList<BigDecimal>();
		BigDecimal rateVal = getRateVal(rate).getRateValue();
		if (rateVal != null) {
			BigDecimal interest = cash.multiply(rateVal);
			result.add(interest);// 添加利息
			result.add(interest.add(cash));// 添加总金额
		}
		return result;
	}

	/**
	 * 计算取款时实际所得利息
	 * @param user
	 * @param card
	 * @param depositTime
	 *        时间为UserBusiness的时间
	 * @return list.get(0):利息
	 *         list.get(1):最终金额
	 */
	public List<BigDecimal> calculateActualInterestAndResultMoney(User user, Card card,
			String depositTime, Rate rate) {
		BigDecimal resultMoney = new BigDecimal(0);
		Date startTime = getParseDate(depositTime);
		UserBusiness ubInfo = getUserBusinieeInfo(user, card, startTime);
		List<BigDecimal> result = null;

		if (ubInfo != null) {
			result = new LinkedList<BigDecimal>();
			Date endTime = ubInfo.getWithdrawalsTime();
			// 得到交易记录中的交易金额，即为用户所存金额
			TradeExample tradeExample = new TradeExample();
			cn.jxau.fukin.model.TradeExample.Criteria criteria = tradeExample.createCriteria();
			criteria.andTradeIdEqualTo(ubInfo.getTradeId());
			List<Trade> trade = tradeMapper.selectByExample(tradeExample);
			BigDecimal tradeAmount = trade.get(0).getTradeAmount();

			// 两种情况，一种满了定期时间的，一种没到定期时间
			if (System.currentTimeMillis() - endTime.getTime() >= 0) {
				// 取款时间超过定期时间
				resultMoney = getArrivalDeadlineMoney(tradeAmount, endTime, rate);
			} else {
				// 没到定期时间
				resultMoney = getNotArrivalDeadlineMoney(tradeAmount, startTime);
			}
			result.add(resultMoney);
			result.add(resultMoney.add(tradeAmount));
		}

		return result;
	}

	/**
	 * 存款操作
	 * @param user
	 * @param card
	 * @param rate
	 */
	public String deposit(Card card, Rate rate, String money) {
		Card bankCard = cardMapper.selectByPrimaryKey(card.getcId());
		if (bankCard.getcPassword().equals(card.getcPassword())) {
			BigDecimal cash = new BigDecimal(money);
			String tradeId = PKUtil.PrimaryKey();

			// 1.将交易记录存入到交易表
			saveTrade(cash, tradeId, bankCard.getUser());
			// 2.根据存款时间得和定期时间得到取款时间,然后存入用户业务表
			saveUserBusiness(card.getUser(), card, rate, tradeId);
			// 3.更新银行卡中的余额和活动金额
			updateMoneyOfCard(card, rate, cash);
			// 4.将存钱信息存入数据库，以便发送给用户
			saveNotify(bankCard.getUser(), "存款操作", "存款金额为：" + money);
			return SUCCESS;
		} else {
			return ERROR;
		}

	}

	/**
	 * 取款或操作
	 * @param user
	 * @param card
	 * @param money
	 * @param rate
	 *        (若活期现金够时输入：null)
	 * @param depositTime
	 *        (若活期现金够时输入：null)
	 * @return
	 */
	public String withdraw(Card card, String money, Rate rate, String depositTime) {
		Card bankCard = cardMapper.selectByPrimaryKey(card.getcId());
		// 校验交易密码
		if (bankCard.getcPassword().equals(card.getcPassword())) {
			BigDecimal cash = new BigDecimal(money);
			// 默认使用活期的流动资金
			if (bankCard.getcActivityCash().compareTo(cash) >= 0) {// 活期现金足够
				bankCard.setcActivityCash(bankCard.getcActivityCash().subtract(cash));
			} else {
				if (rate == null || depositTime == null) {
					return "活期现金不足，请选择取哪比定期现金！";
				} else {
					List<BigDecimal> resultMoney = calculateActualInterestAndResultMoney(
							bankCard.getUser(), card, depositTime, rate);// 这里就算一笔定期交易的了，多笔感觉越算越复杂，捂脸哭
					BigDecimal totalMoney = resultMoney.get(0);
					if (totalMoney.compareTo(cash) >= 0) {
						// 将余额设为余额减去取款金额
						bankCard.setcBalance(bankCard.getcBalance().subtract(cash));

						// 更改当前定期交易记录的金额
						updataTrade(bankCard.getUser(), card, depositTime,
								totalMoney.subtract(cash));
					} else {
						return "当前该定期金额及利息不足,请减少单笔金额";
					}
				}
			}
			cardMapper.updateByPrimaryKey(bankCard);
		} else {
			return ERROR;
		}
		return SUCCESS;
	}

	// 转账
	public String transfer(Card myCard, Card otherCard, String money, Rate rate, String depositTime) {
		// 1.先减钱
		if (withdraw(myCard, money, rate, depositTime).equals("error")) {
			return ERROR;
		}
		// 2.再加钱，向银行卡中添加金额
		Card otherBankCard = cardMapper.selectByPrimaryKey(otherCard.getcId());
		BigDecimal cash = new BigDecimal(money);
		otherBankCard.setcActivityCash(otherBankCard.getcActivityCash().add(cash));
		cardMapper.updateByPrimaryKey(otherBankCard);
		// 3.增加交易记录
		User user = myCard.getUser();
		User other = otherCard.getUser();
		addTradeRecord(user, other, cash, "转账");
		// 4.添加通知
		saveNotify(user, "转帐通知", new StringBuilder("你已成功向").append(other.getuName()).append("转账:")
				.append(money).append("元").toString());
		saveNotify(other, "转帐通知", new StringBuilder("你已成功收到").append(user.getuName()).append("转账:")
				.append(money).append("元").toString());

		return SUCCESS;
	}

	// 添加交易记录
	private void addTradeRecord(User user, User other, BigDecimal money, String type) {
		Trade trade = new Trade();
		trade.setTradeId(getPrimaryKey());
		trade.setuId(user.getuId());
		trade.setAcceptId(other.getuId());
		trade.setTradeType(type);
		trade.setTradeAmount(money);
		trade.setTradeTime(new Date());
		tradeMapper.insert(trade);
	}

	/**
	 * 更新交易记录
	 * @param user
	 * @param card
	 * @param depositTime
	 * @param money
	 */
	private void updataTrade(User user, Card card, String depositTime, BigDecimal money) {
		Date startTime = getParseDate(depositTime);
		UserBusiness ubInfo = getUserBusinieeInfo(user, card, startTime);
		String tradeId = ubInfo.getTradeId();
		Trade trade = tradeMapper.selectByPrimaryKey(tradeId);
		trade.setTradeAmount(money);
		tradeMapper.updateByPrimaryKey(trade);
	}

	// 将通知信息保存到数据库
	private void saveNotify(User user, String title, String content) {
		Notify notify = new Notify();
		notify.setNotifyId(getPrimaryKey());
		notify.setNotifyTitle(title);
		notify.setNotifyContent(content);
		notify.setuId(user.getuId());
		notifyMapper.insert(notify);
	}

	// 将钱存入银行卡
	private void updateMoneyOfCard(Card card, Rate rate, BigDecimal money) {
		Card bankCard = cardMapper.selectByPrimaryKey(card.getcId());
		if (CURRENT.equals(rate.getDepositType())) {
			bankCard.setcActivityCash(bankCard.getcActivityCash().add(money));
		} else if (PERIODICAL.equals(rate.getDepositType())) {
			// bankCard.setcBalance(bankCard.getcBalance().add(money));
		} else {
			throw new ServiceException("汇率表中存款类型出错!");
		}
		bankCard.setcBalance(bankCard.getcBalance().add(money));
		cardMapper.updateByPrimaryKey(bankCard);
	}

	// 保存用户业务信息
	private void saveUserBusiness(User user, Card card, Rate rate, String tradeId) {
		Date depositTime = new Date();
		Date withdrawalTime = getEndTime(depositTime, getRateVal(rate).getDepositDeadline());
		UserBusiness userBusiness = new UserBusiness();
		userBusiness.setcId(card.getcId());
		userBusiness.setuId(user.getuId());
		userBusiness.setDepositTime(depositTime);
		userBusiness.setWithdrawalsTime(withdrawalTime);
		userBusiness.setTradeId(tradeId);
		businessMapper.insert(userBusiness);
	}

	// 保存交易记录
	private void saveTrade(BigDecimal cash, String tradeId, User user) {
		Trade trade = new Trade();
		trade.setTradeAmount(cash);
		trade.setTradeId(tradeId);
		trade.setTradeType(DEPOSIT);
		trade.setuId(user.getuId());
	}

	// 得到用户业务信息
	private UserBusiness getUserBusinieeInfo(User user, Card card, Date startTime) {
		UserBusinessExample businessExample = new UserBusinessExample();
		cn.jxau.fukin.model.UserBusinessExample.Criteria criteria = businessExample
				.createCriteria();
		criteria.andUIdEqualTo(user.getuId());
		criteria.andCIdEqualTo(card.getcId());
		criteria.andDepositTimeEqualTo(startTime);
		List<UserBusiness> list = businessMapper.selectByExample(businessExample);
		if (list != null && !list.isEmpty()) {
			return list.get(0);
		}
		return null;
	}

	// 得到未到定期的钱
	private BigDecimal getNotArrivalDeadlineMoney(BigDecimal tradeAmount, Date startTime) {
		Map<String, Integer> map = getYearMonthAndDay(startTime, new Date());
		return getCurrent(map, tradeAmount, startTime);
	}

	// 满了定期时间
	private BigDecimal getArrivalDeadlineMoney(BigDecimal tradeAmount, Date endTime, Rate rate) {
		// 1.先计算到定期时所得的总金额
		List<BigDecimal> interest = calculatePreInterest(rate, tradeAmount.toString());
		BigDecimal totalMoney = interest.get(1);

		// 2.再计算从定期截止日期到现在所剩下的天数，用活期利率*天数*总金额
		Map<String, Integer> map = getYearMonthAndDay(new Date(), endTime);
		BigDecimal resMoney = getCurrent(map, totalMoney, endTime);

		return totalMoney.add(resMoney);
	}

	// 计算活期金额
	private BigDecimal getCurrent(Map<String, Integer> map, BigDecimal totalMoney, Date endTime) {
		int days = getTotalDaysByDate(map.get("year"), map.get("month"), map.get("day"));
		Rate rate = new Rate();
		rate.setDepositType(CURRENT);
		rate.setDepositDeadline(ONEYEAR);
		BigDecimal rateVal = getRateVal(rate).getRateValue();// 得到一年的利率
		BigDecimal dayOfInterest = new BigDecimal(days / 360);
		return totalMoney.multiply(rateVal).multiply(dayOfInterest);
	}

	// 根据条件得到利率信息
	private Rate getRateVal(Rate rate) {
		RateExample rateExample = new RateExample();
		Criteria criteria = rateExample.createCriteria();
		judge(rate, criteria);
		List<Rate> rates = rateMapper.selectByExample(rateExample);
		if (rates != null) {
			return rates.get(0);
		}
		return null;
	}

	private void judge(Rate rate, Criteria criteria) {
		if (rate.getRateId() != null && !"".equals(rate.getRateId().trim())) {
			criteria.andRateIdEqualTo(rate.getRateId());
		}
		if (rate.getDepositType() != null && !"".equals(rate.getDepositType().trim())) {
			criteria.andDepositTypeEqualTo(rate.getDepositType());
		}
		if (rate.getDepositDeadline() != null && !"".equals(rate.getDepositDeadline().trim())) {
			criteria.andDepositDeadlineEqualTo(rate.getDepositDeadline());
		}
		if (rate.getRateValue() != null) {
			criteria.andRateValueEqualTo(rate.getRateValue());
		}
	}
}
